import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.hql.internal.ast.SqlASTFactory;

import model.ConnectionSingleton;
import model.Contient;
import model.Keyword;
import model.KeywordHome;
import model.Paragraphe;
import model.ParagrapheHome;
import model.Word;
import model.WordHome;


public class SearchIt {

	private class Expression{
		private String[] exp;

		public String[] getExp() {
			return exp;
		}

		public void setExp(String[] exp) {
			this.exp = exp;
		}

		public Expression(String exp[]) {
			this.exp = exp;
		}
		
		public Expression(String exp) {
			this.exp = new String[1];
			this.exp[0] = exp;
		}

		@Override
		public boolean equals(Object obj) {
			if(!(obj instanceof Expression)){
				return false;
			}
			boolean equals = true;
			for (int i = 0; i < exp.length && equals; i++) {
				String s1 = exp[i];
				String s2 = ((Expression) obj).exp[i];
				if(s1.length() > 6){
					s1 = s1.substring(0,6);
				}
				if(s2.length() > 6){
					s2 = s2.substring(0,6);
				}
				if(!s2.equalsIgnoreCase(s1)){
					equals = false;
				}
			}
			return equals;
		}

		public int getLength() {
			return exp.length;
		}
		
		
	}
	private List<String> stopList;
	
	public static void main(String args[]) throws Exception {
		SearchIt s = new SearchIt();
		//s.indexer();
		s.chercherTout();
		//test
	}
	
	private void chercherTout() {
		ConnectionSingleton conn = ConnectionSingleton.getInstance();
		conn.beginTransaction();
		QueriesParser d = new QueriesParser("documents/Queries/queries.xml");
		HashMap<String, String> queries = d.getQuerys();
		ParagrapheHome paragrapheHome = new ParagrapheHome();
		List<Paragraphe> liste_para_complete = paragrapheHome.findAll(Paragraphe.class);
		for(String i : queries.keySet()){
			System.out.println("Query "+i);
			Collection<Pair> list = chercher(queries.get(i).trim());
			//GenerateQrel.Generer(i, liste_para_complete, list);
			GenerateQrel.CalculerRappel("documents/qrels/qrel"+i.substring(1)+".txt", list);
			
		}
		conn.getT().commit();
	}

	public SearchIt() {
		stopList = new ArrayList<String>();
		readStopList();
	}
		
	public void indexer(){
		ConnectionSingleton conn = ConnectionSingleton.getInstance();
		conn.beginTransaction();
		HashMap<String, Integer> keywords = new HashMap<String, Integer>();
		HashMap<String, Contient> contients = new HashMap<String, Contient>();
		KeywordHome keywordHome = new KeywordHome();
		List<Keyword> k = keywordHome.findAll(Keyword.class);
		for (Keyword keyword : k) {
			keywords.put(keyword.getKeyword(), keyword.getIdKeyword());
		}
		try {
			String file = "documents/Collection/d";
			for (int i = 1; i < 104; i++) {
				//while(i == 40 || i == 41 || i == 42 || i == 44 ||i == 45 || i == 47 ||i == 49 || i == 50 ||i == 52 || i == 53 ||i == 55 ||i == 56 ||i == 58 ||i == 59 ||i == 61 ||i == 62 ||i == 63 ||i == 64 ||i == 67 ||i == 68||i == 69 ||i == 70||i == 71||i == 72){
				while(i == 40 || i == 41 || i == 42 ){
					i++;
				}
	
				String append = "";
				if(i < 10){
					append = "00"+i;
				}
				else if(i < 100){
					append = "0"+i;
				}
				else{
					append = ""+i;
				}
				
					DocumentParser d = new DocumentParser(file+append+".xml",keywords,contients);
					d.parserDocument();
			}
			System.out.println("Appel calculNbParagraphes");
			Session s = conn.getS();
			SQLQuery q = s.createSQLQuery("CALL `searchit`.`calculNbParagraphes`()");
			q.executeUpdate();
			System.out.println("Appel calculFrequence");
			SQLQuery q2 = s.createSQLQuery("CALL `searchit`.`calculFrequence`()");
			q2.executeUpdate();
			System.out.println("Appel calculPrevNext");
			SQLQuery q3 = s.createSQLQuery("CALL `searchit`.`calculPrevNext`()");
			q3.executeUpdate();
			conn.getT().commit();
		} catch (Exception e) {
			conn.getT().rollback();
			writeToFile("documents/c.txt","Document");
		}
	}
	
	public static void writeToFile(String path,String text) {
		try {
			BufferedWriter bw = new BufferedWriter(new FileWriter(new File(path), true));
			bw.write(text);
			bw.newLine();
			bw.close();
		} catch (Exception e) {
		}
	}
	
	
	
	public Collection<Pair> chercher(String query){
	    long begin = System.currentTimeMillis();
	    //extraction des expressions de la requete
		HashMap<String, Integer> q_tfidf = extractExpressions(getQueryExplosed(query), query.length(), new HashMap<String,Integer>());
		HashMap<String, Integer> q_tfidf2 = new HashMap<String,Integer>();
		for(String queryTemp : q_tfidf.keySet()){
			Iterable<Map<String, String>> res = rechercherSynonymes(queryTemp);
			for(Map<String, String> r :res){
				if(!q_tfidf.containsKey(r.get("label"))){
					q_tfidf2.put(r.get("label"), q_tfidf.get(queryTemp)/2);
				}
			}
			rechercherInstances(queryTemp);
		}
		q_tfidf.putAll(q_tfidf2);
		KeywordHome keywordHome = new KeywordHome();
		HashMap<Paragraphe, HashMap<Expression, Float>> expressions = new HashMap<Paragraphe,HashMap<Expression, Float>>();
		List<Pair> paragraphes = new ArrayList<Pair>();
		//pour chaque expression
		for(String queryKeyword : q_tfidf.keySet()){
			//si c'est une expression à plusieurs termes
			if(queryKeyword.split("\\s").length > 1){
				String[] keywords_expression_temp = queryKeyword.split("\\s");
				String[] keywords_expression = new String[keywords_expression_temp.length];
				int i = 0;
				for (String string : keywords_expression_temp) {
					if(string.length() >=6){
						keywords_expression[i] = string.substring(0, 6).toUpperCase();
					}
					else{
						keywords_expression[i] = string.toUpperCase();
					}
					i++;
				}
				List<Keyword> first_keyword = keywordHome.findByExample(new Keyword(keywords_expression[0],null,null));
				//le terme existe
				if(first_keyword.size() == 1){
					Keyword k = first_keyword.get(0);
					float nb_occurences_total = 0;
					long begin2 = System.currentTimeMillis();
					Set<Contient> conts = k.getContients();
					for (Contient contient : conts) {
						float nb_occurences = numberOccurrencesExpressionInParagraphe(keywords_expression,contient);
						if(nb_occurences>0){
							nb_occurences_total += nb_occurences;
							float tf_expression = nb_occurences/contient.getParagraphe().getLongueur().floatValue();
							if(expressions.get(contient.getParagraphe()) == null){
								expressions.put(contient.getParagraphe(), new HashMap<Expression,Float>());
								expressions.get(contient.getParagraphe()).put(new Expression(keywords_expression), tf_expression);
							}
							else{
								expressions.get(contient.getParagraphe()).put(new Expression(keywords_expression), tf_expression);
							}
						}
					}
					long end2 = System.currentTimeMillis();
				    float time2 = ((float) (end2-begin2)) / 1000f;
					if(nb_occurences_total > 0){
						for (Paragraphe p : expressions.keySet()) {
							HashMap<Expression, Float> hashMap = expressions.get(p);
							if(hashMap.get(new Expression(keywords_expression)) != null){
								float tfidf = hashMap.get(new Expression(keywords_expression))/nb_occurences_total;
								expressions.get(p).put(new Expression(keywords_expression),tfidf*new Expression(keywords_expression).getLength());
							}
						}
					}
				}
			}
			else{
				String s;
				if(queryKeyword.length() >=6){
					s = queryKeyword.substring(0, 6).toUpperCase();
				}
				else{
					s = queryKeyword.toUpperCase();
				}
				List<Keyword> keyword = keywordHome.findByExample(new Keyword(s,null,null));
				Set<Contient> contients = new HashSet<Contient>();
				if(keyword.size() == 1){
					contients  = keyword.get(0).getContients();
				}
				for(Contient c : contients){
					if(expressions.get(c.getParagraphe()) == null){
						expressions.put(c.getParagraphe(), new HashMap<Expression,Float>());
						expressions.get(c.getParagraphe()).put(new Expression(c.getKeyword().getKeyword()),(c.getFrequence().floatValue()/c.getParagraphe().getLongueur().floatValue())/c.getKeyword().getNbParagraphes().floatValue());
					}
					else{
						expressions.get(c.getParagraphe()).put(new Expression(c.getKeyword().getKeyword()),(c.getFrequence().floatValue()/c.getParagraphe().getLongueur().floatValue())/c.getKeyword().getNbParagraphes().floatValue());
					}
				}
			}
		}
		for (Paragraphe p :expressions.keySet()) {
			float tfidf_cumule = 0;
			for (Expression exp : expressions.get(p).keySet()) {
				tfidf_cumule += expressions.get(p).get(exp);
			}
			paragraphes.add(new Pair(p, tfidf_cumule));
		}
		Collections.sort(paragraphes,new PairComparator());
		System.out.println(paragraphes.size());
		long end = System.currentTimeMillis();
	    float time = ((float) (end-begin)) / 1000f;
	    System.out.println("Temps : " + time);
		return paragraphes;
	}
	
	private Iterable<Map<String,String>> rechercherInstances(String queryTemp) {
		SparqlClient sp = new SparqlClient("127.0.0.1:3030/space");
		String prefixes = "PREFIX : <http://ontologies.alwaysdata.net/space#> " +
			"PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> " +
			"PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> " +
			"PREFIX owl: <http://www.w3.org/2002/07/owl#> " +
			"PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> " +
			"PREFIX balade: <http://www.semanticweb.org/ontologies/2011/11/Balade.owl#> ";
		String select = "SELECT ?label WHERE{" +
			"?obj rdfs:label ?labeltemp." +
			" ?obj rdfs:label ?label " +
			"filter regex(?labeltemp, \"^"+queryTemp+"$\", \"i\")" +
			"}";
		Iterable<Map<String,String>> res = sp.select(prefixes+select);
		return res;
		
	}

	private Iterable<Map<String, String>> rechercherSynonymes(String queryTemp) {
		SparqlClient sp = new SparqlClient("127.0.0.1:3030/space");
		String prefixes = "PREFIX : <http://ontologies.alwaysdata.net/space#> " +
				"PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> " +
				"PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> " +
				"PREFIX owl: <http://www.w3.org/2002/07/owl#> " +
				"PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> " +
				"PREFIX balade: <http://www.semanticweb.org/ontologies/2011/11/Balade.owl#> ";
		String select = "SELECT ?label WHERE{?obj rdfs:label ?labeltemp. ?obj rdfs:label ?label filter regex(?labeltemp, \"^"+queryTemp+"$\", \"i\")}";
		Iterable<Map<String,String>> res = sp.select(prefixes+select);
		return res;
	}

	private int numberOccurrencesExpressionInParagraphe(String[] keywords_expression,Contient contient) {
		WordHome wordHome = new WordHome();
		Set<Word> words =  contient.getWords();
		int nb_occurences = 0;
		HashMap<Word, List<Word>> expressions = new HashMap<Word, List<Word>>();
		for (Word word : words) {
			expressions.put(word, new ArrayList<Word>());
			int end = word.getPosition() + keywords_expression.length;
			Word curr_word = word;
			for (int i = word.getPosition(); i < end; i++) {
				if(curr_word != null){
					expressions.get(word).add(curr_word);
				}
				curr_word = word.getWordByIdNext();
			}
		}
		for (Word word : expressions.keySet()) {
			if(matchExpressions(keywords_expression, expressions.get(word))){
				nb_occurences++;
			}
		}
		return nb_occurences;
	}

	private boolean matchExpressions(String[] keywords_expression,List<Word> words) {
		if(keywords_expression.length != words.size()){
			return false;
		}
		boolean match = true;
		for (int i = 0; i < keywords_expression.length && match; i++) {
			Word word = words.get(i);
			if(word != null){
				String word_value = word.getValue();
				if(word_value.length() >=6){
					word_value = word_value.substring(0, 6).toUpperCase();
				}
				else{
					word_value = word_value.toUpperCase();
				}
				if(!keywords_expression[i].equalsIgnoreCase(word_value)){
					match = false;
				}
			}
			else{
				match = false;
			}
		}
		return match;
	}

	private String[] getQueryExplosed (String query){
		String[] explosedQ = query.split("[\\s|,|`|‘|\\.|;|\\?|:|\\-|\\*|°|\\/|\\+|\\$|£|€|\\d|\\!|'|\"|«|»|\\[|\\]|\\(|\\)|~|\\{|\\}]+");
		String[] finalQ = new String[query.length()];
		int i = 0;
		for (String q : explosedQ){
			if (notInStopList(q)) {
				finalQ[i]=q;
				i++;
			}
		}
		return finalQ;
	}
	
	private boolean notInStopList(String word) {
		boolean not_in = true;
		for (String stop_word : stopList) {
			if(word.compareToIgnoreCase(stop_word) == 0){
				not_in = false;
			}
		}
		return not_in;
	}
	
	private void readStopList() {
		try{
			  // Open the file that is the first 
			  // command line parameter
			  FileInputStream fstream = new FileInputStream("documents/stopliste.txt");
			  // Get the object of DataInputStream
			  DataInputStream in = new DataInputStream(fstream);
			  BufferedReader br = new BufferedReader(new InputStreamReader(in));
			  String strLine;
			  //Read File Line By Line
			  while ((strLine = br.readLine()) != null){
				  stopList.add(strLine);
			  }
			  //Close the input stream
			  in.close();
			}catch (Exception e){//Catch exception if any
				  System.err.println("Error: " + e.getMessage());
			}
		
	}
	
	private HashMap<String,Integer> extractExpressions(String[] query, int lenght,HashMap<String,Integer> tab ){
		if (lenght == 0) {
			return tab;
		}
		for (int i=0; i + lenght < query.length ;i++){
			StringBuilder expr = new StringBuilder();
			for (int j=i;j<lenght;j++){
				if(query[j] != null){
					expr.append(query[j]);
					expr.append(" ");
				}
			}
			if(!expr.toString().trim().equals("")){
				tab.put(expr.toString().trim(), 1);
			}
		}
		return extractExpressions(query, lenght - 1, tab);		
	}
	

}
